import java.net.*;
import java.io.*;
import java.lang.*;

public class MigrationManager implements Runnable 
{
	public static final int MIGRATION_PORT = 4567;

	//migration type
	public static final int AGENT_ARRIVE = 1;
	public static final int AGENT_LEAVE = 2;


	Server server = null;
	ServerSocket migrationServer = null;
	
	public MigrationManager(Server server) {
		this.server = server;
		try{
			migrationServer = new ServerSocket(MIGRATION_PORT);
		}catch(IOException ex) {
			System.out.println(ex);
			System.exit(1);
		}
	}

	public void run()
	{
		DataInputStream in = null;
		Socket connect = null;
		int type = 0;
		while (true) {
			try{
				connect = migrationServer.accept();
				in = new DataInputStream(connect.getInputStream());
				type = in.readInt();
				switch (type) {
					case AGENT_ARRIVE:
						agentArrive(connect);
						break;
					case AGENT_LEAVE:
						agentLeave(connect);
						break;
					default:
						break;
				}
			}catch(IOException ex) {
				System.out.println(ex);
			}finally{
				try{
					if (connect!=null)
						connect.close();
				}catch(IOException ex) { }
			}
		}
	}

	private void agentArrive(Socket connect) {
		Agent agent = null;
		ObjectInputStream obin = null;
		DataInputStream din = null;

		/* read Agent from socket*/
		try{
			obin = new ObjectInputStream(connect.getInputStream());
			agent = (Agent)obin.readObject();
		}catch(IOException ex) {
			System.out.println(ex);
		}catch(ClassNotFoundException ex){
			System.out.println(ex);
		}finally{
			try{
				if (obin!=null)
					obin.close();
			}catch(IOException ex) { }
		}

		String className;
		Class<? extends Behaviour> bclass = null;
		try{
			din = new DataInputStream(connect.getInputStream());
			className = din.readUTF();
			BehaviourLoader loader = new BehaviourLoader(connect);
			bclass = loader.loadClass(className);
			Behaviour behaviour = (Behaviour)bclass.newInstance();
			agent.setBehaviour(behaviour);

			Thread runAgent = new Thread(agent);
			runAgent.start();

		}catch(IOException ex) {
			System.out.println(ex);
		}catch(InstantiationException ex) {
			System.out.println(ex);
		}catch(IllegalAccessException ex) {
			System.out.println(ex);
		}

	}

	private void agentLeave(Socket connect) {
		DataInputStream din = null;
		int id=0;

		//get the agent ID
		try{
			din = new DataInputStream(connect.getInputStream());
			id = din.readInt();
		}catch(IOException ex) {
			System.out.println(ex);
		}

		//Find the Agent from manger
		Manager manager = server.getManager();
		Agent agent = manager.findAgentByID(id);
		if (agent == null) 
			return ;

		manager.unregisterAgent(agent);

		//send the Agent to network
		Socket sock = null;
		DataOutputStream dout = null;
		PID pid = manager.getRandomCity();

		try{
			sock = new Socket(pid.getIP(), MigrationManager.MIGRATION_PORT);
			dout = new DataOutputStream(sock.getOutputStream());
			dout.writeInt(MigrationManager.AGENT_LEAVE); //type
			agent.serializeToNetwork(sock);
		}catch(IOException ex) {
			System.out.println(ex);
		}finally {
			try{
				if (dout!=null)
					dout.close();
			}catch(IOException ex) { }
		}

	}


	public static void test() {
	}

	public static void main(String[] args)
	{
	}

}
